package br.ufrgs.rescue.agent;

import br.ufrgs.rescue.model.AmbulanceState;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;

import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.ChangeSet;
import rescuecore2.messages.Command;
import rescuecore2.log.Logger;

import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.standard.entities.AmbulanceTeam;
import rescuecore2.standard.entities.Human;
import rescuecore2.standard.entities.Civilian;
import rescuecore2.standard.entities.Refuge;
import sample.DistanceSorter;

/**
 * A ambulance team agent developed by Ricardo Grunitzki.
 */
public class MyAmbulanceTeam extends MyAbstractAgent<AmbulanceTeam> {

    private Collection<EntityID> unexploredBuildings;
    private AmbulanceState estado = AmbulanceState.FINDING;
    private List<Human> humansToRescue = new ArrayList<Human>();

    @Override
    public String toString() {
        return "Sample ambulance team";
    }

    @Override
    protected void postConnect() {
        super.postConnect();
        model.indexClass(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.REFUGE, StandardEntityURN.BUILDING);
        unexploredBuildings = new HashSet<EntityID>(buildingIDs);
    }

    @Override
    protected void think(int time, ChangeSet changed, Collection<Command> heard) {
        if (time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)) {
            // Subscribe to channel 1
            sendSubscribe(time, 1);
        }
        for (Command next : heard) {
            Logger.debug("Heard " + next);
        }

        //System.out.print(me().getID() + "");
        switch (estado) {
            case FINDING:
                //System.out.print("\t" + estado.toString());
                if (current_target == null) {
                    //System.out.print("\tprocurando humanos");

                    List<Human> humans = getTargets();
                    for (Human next : humans) {
                        //System.out.print("\thumanos aqui");
                        //System.out.print("\tinsere na lista");
                        //insere humanos na lista de humanos a resgatar
                        humansToRescue.add(next);
                    }
                    current_target = humanToRescue();
                    if (current_target != null) {
                        //System.out.print("\talvo atualizado");
                        estado = AmbulanceState.RESCUING;
                        //go to rescuing case
                    } else {
                        //System.out.print("\tnão tenho alvo, vou explorar");
                        sendMove(time, randomWalkonStreet());
                        break;
                    }
                } else {
                    //System.out.print("\ttenho alvo, vou resgatar");
                    //rescuing
                    estado = AmbulanceState.RESCUING;
                }
            case RESCUING:
                //System.out.print("\t" + estado.toString());
                //System.out.print("\testou resgatando?");
                if (current_target != null) {
                    //System.out.print("\tsim");
                    //tem alguém a bordo?
                    //System.out.print("\talguém abordo?");
                    if (!someoneOnBoard()) {
                        //System.out.print("\tnão");
                        if (humanById(current_target).getPosition().equals(location().getID())) {
//VERIFICAR SE ESTOU NO REFUGIO
                            if (location() instanceof Refuge) {
                                //System.out.print("\testou no refugio, descarregaer");
                                estado = AmbulanceState.FINDING;
                                //update human to rescue
                                //System.out.print("\tremovendo humano da lista");
                                updateHumanToRescue(current_target);
                                current_target = null;
                                sendUnload(time);
                                break;
                            }

                            //System.out.print("\testou nele");
                            if ((humanById(current_target) instanceof Civilian) && humanById(current_target).getBuriedness() == 0 && !(location() instanceof Refuge)) {
                                // carregar
                                //System.out.print("\tprecisa ser carregado");
                                sendLoad(time, humanById(current_target).getID());
                                break;
                            }
                            if (humanById(current_target).getBuriedness() > 0) {
                                // Resgate
                                //System.out.print("\tprecisa ser desenterrado");
                                sendRescue(time, humanById(current_target).getID());
//IMPRIMIR A QUANITDADE DE TERRA SOBRE O CIVIL                                //System.out.print("\t" + humanById(current_target).get);
                                break;
                            }
                        } else {
                            // Tenta ir até o alvo
                            // atualizar alvo
                            current_target = humanToRescue();
                            //System.out.print("\tpreciso caminhar até o alvo");
                            List<EntityID> path = search.breadthFirstSearch(me().getPosition(), humanById(current_target).getPosition());
                            if (path != null) {
                                //System.out.print("\testou indo até o alvo");
                                sendMove(time, path);
                                break;
                            }
                            current_target = null;
                            sendMove(time, randomWalkonStreet());
                            //System.out.print("\tnão consegui ir até o alvo");
                        }
                    } else {
                        //System.out.print("\testou no refúgio?");
                        if (location() instanceof Refuge) {
                            //System.out.print("\tsim, descarregar?");
                            current_target = humanToRescue();
                            sendUnload(time);
                            break;
                        }
                        //System.out.println("\tnão");
                        //System.out.print("\ttrasportar até o refúgio");
                        estado = AmbulanceState.RESCUING;
                        //System.out.print("\tindo para refugio");
                        List<EntityID> path = search.breadthFirstSearch(me().getPosition(), refugeIDs);
                        if (path != null) {
                            sendMove(time, path);
                            break;
                        }
                    }
                } else {
                    //System.out.print("\tnão tenho alvo, vou explorar");
                    estado = AmbulanceState.FINDING;
                    break;
                    //mandar se mover para algum lugar pra aproveitar o timestep
                }

            default:
                current_target = null;
//                //System.out.print("\t" + estado.toString());
//                //System.out.print("\tcarregando alguém?");
//                //Estou carregando alguém?
//                if (someoneOnBoard()) {
//                    //System.out.print("\tsim");
//                    //System.out.print("\trefugio?");
//                    // Estou no refúgio?
//                    if (location() instanceof Refuge) {
//                        //System.out.print("\tsim, descarregar");
//                        // Descarregar!
//                        sendUnload(time);
//                        estado = AmbulanceState.FINDING;
//                        break;
//                    } else {
//                        //System.out.print("\tnão, ir para refugio");
//                        // Ir para o refúgio
//                        if (location() instanceof Refuge) {
//                            estado = AmbulanceState.FINDING;
//                            sendUnload(time);
//                            break;
//                        }
//                        List<EntityID> path = search.breadthFirstSearch(me().getPosition(), refugeIDs);
//                        if (path != null) {
//                            sendMove(time, path);
//                            break;
//                            //é possível que não consiga criar caminho para o refúgio
//                        }
//                    }
//                }
                break;
        }
//
//        updateUnexploredBuildings(changed);
        //System.out.println("");
    }

    @Override
    protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
        return EnumSet.of(StandardEntityURN.AMBULANCE_TEAM);
    }

    private boolean someoneOnBoard() {
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN)) {
            if (((Human) next).getPosition().equals(getID())) {
                Logger.debug(next + " is on board");
                return true;
            }
        }
        return false;
    }

    private List<Human> getTargets() {
        List<Human> targets = new ArrayList<Human>();
        for (StandardEntity next : model.getEntitiesOfType(StandardEntityURN.CIVILIAN, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.POLICE_FORCE, StandardEntityURN.AMBULANCE_TEAM)) {
            Human h = (Human) next;
            if (h == me()) {
                continue;
            }
            if (h.isHPDefined()
                    && h.isBuriednessDefined()
                    && h.isDamageDefined()
                    && h.isPositionDefined()
                    && h.getHP() > 0
                    && (h.getBuriedness() > 0 || h.getDamage() > 0)) {
                targets.add(h);
            }
        }
        Collections.sort(targets, new DistanceSorter(location(), model));
        return targets;
    }

    private void updateUnexploredBuildings(ChangeSet changed) {
        for (EntityID next : changed.getChangedEntities()) {
            unexploredBuildings.remove(next);
        }
    }

    private Human humanById(EntityID entityID) {
        Human h = (Human) model.getEntity(entityID);
        return h;
    }

    private EntityID humanToRescue() {
        if (humansToRescue.size() > 0) {
            return humansToRescue.get(0).getID();
        } else {
            return null;
        }
    }

    private void updateHumanToRescue(EntityID id) {
        for (Human h : humansToRescue) {
            if (h.getID() == id) {
                humansToRescue.remove(h);
                //System.out.print("\thumano removido da lista");
                return;
            }
        }
    }
}
////TRANSPORTING        
//        // Estou transportando um civil para o refúgio?
//        if (someoneOnBoard()) {
//            // Estou no refúgio?
//            if (location() instanceof Refuge) {
//                // Descarregar!
//                Logger.info("Unloading");
//                sendUnload(time);
//                return;
//            } else {
//                // Ir para o refúgio
//                List<EntityID> path = search.breadthFirstSearch(me().getPosition(), refugeIDs);
//                if (path != null) {
//                    Logger.info("Moving to refuge");
//                    sendMove(time, path);
//                    return;
//                }
//                // O que eu faço agora? Poderia muito bem continuar e ver se podemos escavar alguém
//                Logger.debug("Failed to plan path to refuge");
//            }
//        }
//FINDING
// Passar por metas (ordenadas por distância) e verificar se há coisas que podemos fazer
//        for (Human next : getTargets()) {
//            if (next.getPosition().equals(location().getID())) {
//                // Alvos no mesmo lugar podem precissar de resgate ou ser carregado
//                if ((next instanceof Civilian) && next.getBuriedness() == 0 && !(location() instanceof Refuge)) {
//                    // carregar
//                    Logger.info("Loading " + next);
//                    sendLoad(time, next.getID());
//                    return;
//                }
//                if (next.getBuriedness() > 0) {
//                    // Resgate
//                    Logger.info("Rescueing " + next);
//                    sendRescue(time, next.getID());
//                    return;
//                }
//            } else {
//                // Tenta ir até o alvo
//                List<EntityID> path = search.breadthFirstSearch(me().getPosition(), next.getPosition());
//                if (path != null) {
//                    Logger.info("Moving to target");
//                    sendMove(time, path);
//                    return;
//                }
//            }
//        }
//
// Nada para fazer
//        List<EntityID> path = search.breadthFirstSearch(me().getPosition(), unexploredBuildings);
//        if (path != null) {
//            Logger.info("Searching buildings");
//            sendMove(time, path);
//            return;
//        }
//        Logger.info("Moving randomly");
//        sendMove(time, randomWalk());
